/*
 * Copyright (C) 2019 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Task } from "./Task"

import { hilog } from "@kit.PerformanceAnalysisKit";

export class TaskQueue {
  private lastPromise: Promise<void> = Promise.resolve();
  
  /** 待执行的任务列表 */
  private pendingTasks: Task[] = []
  
  /** 当前正在执行的定时器 ID */
  private scheduledTimers: Map<string, number> = new Map()

  /**
   * 调度一个任务执行。
   * 如果任务返回值 > 0，表示需要延迟该纳秒数后再次执行。
   * 如果任务返回值 <= 0 或 -1，表示不需要再次执行。
   */
  schedule(task: Task): void {
    // 如果任务已经在等待中，不重复添加
    if (this.pendingTasks.includes(task)) {
      return
    }
    
    this.pendingTasks.push(task)
    
    this.lastPromise = this.lastPromise
      .then(() => {
        // 检查任务是否被取消
        const index = this.pendingTasks.indexOf(task)
        if (index === -1) {
          return -1 // 任务已被取消
        }
        this.pendingTasks.splice(index, 1)
        return task.runOnce()
      })
      .then(delayNanos => {
        // 如果返回值 > 0，需要延迟后再次执行
        if (delayNanos > 0) {
          // 转换纳秒为毫秒
          const delayMs = Math.max(1, Math.floor(delayNanos / 1000000))
          const timerId = setTimeout(() => {
            this.scheduledTimers.delete(task.name)
            this.schedule(task)
          }, delayMs)
          this.scheduledTimers.set(task.name, timerId as unknown as number)
        }
      })
      .catch(e => {
        hilog.error(0, 'TaskRunner', `Task ${task.name} failed: ${e}`);
        // 不 rethrow，避免中断后续任务
      });
  }

  /**
   * 取消所有待执行的任务。
   * 用于在连接池清空时停止清理任务。
   */
  cancelAll(): void {
    // 清空待执行任务列表
    this.pendingTasks.length = 0
    
    // 取消所有定时器
    for (const timerId of this.scheduledTimers.values()) {
      clearTimeout(timerId)
    }
    this.scheduledTimers.clear()
  }
}